///
/// Author: Greg Sandstrom
/// Email: greg.sandstrom@gmail.com
/// Class: CSCI-399
/// Date: 04/07/2008
/// Lab: 3 (Keyboard Lab)
/// IDE: ZDS II - ZNEO 4.11.1 (Build 08081801)
/// Description: Receives input from a PS2 keyboard, and displays either scancodes
///	  or the value of keys pressed.  The button SW1 switches between scancode and value mode.
///
///   PS/2 data on PD[0]
///   PS/2 clock on PD[1]
///   Mode switch button on PD[3]
///   Speaker on PC[0-1]
///   Status LEDs on PF[0-3]
///
/// Problems: Debugging to the UART wasnt' able to catch all transitions,
///   Having to find other ways to debug.
///  

#include <zneo.h>


#include "clock.h"
#include "led.h"
#include "ledmatrix.h"
#include "ps2scanchar.h"
#include "buttons.h"
#include "rttl_timer.h"
#include "rttl_play.h"

#define MASK_EQ(x,y) ((x&y)==y)

#define SYMBOL_00   0x80 // normal key down
#define SYMBOL_E0   0x81 // extended key down
#define SYMBOL_F0   0x82 // normal key up
#define SYMBOL_E0F0 0x83 // extended key up

#define KEY_SHIFT 0x01 // shift modifer is on
#define KEY_ALT   0x02 // alt modifer is on
#define KEY_CTL   0x04 // control modifier is on

#define DISPLAY_SCANCODE  0x01  // show scancode and key direction on display
#define DISPLAY_CHAR      0x02  // show modifier status, and key pressed

short display_mode = DISPLAY_SCANCODE; // start out in scancode mode

short volatile frame = 0; // scancode frame received
unsigned char volatile frame_status = 0; // frame ready/error

#define FRAME_READY 0x01  // frame_status[0] -> frame ready
#define FRAME_ERROR 0x02  // frame_status[1] -> parity error
#define FRAME_IS_READY ((frame_status & FRAME_READY)>0)
#define SET_FRAME_READ (frame_status &= ~0x03)

void InitPD1AsInterrupt(void);
void InitPD0AsPolled(void);

void AnnounceMode(void);

void main() {
	short f;
	char m = SYMBOL_00;
	int lw=0;
	char key_mods = 0x00;
	char c;
	char* str = "    ";


	InitSystemClock();
	InitLEDs();
	InitLEDMatrixes();
	SetMatrixBuffer("    ");
	Init1Button();
	InitPD1AsInterrupt();
	InitPD0AsPolled();

	init_timer0();
	init_timer1();

	// run the all important command....
	EI();


    while(1) { 
		++lw;
		SetLEDs(frame_status); // visual indicator if we have frames

		if (frame_status & FRAME_READY) {
			f=frame;
			SET_FRAME_READ; // let the next frame be read

			if (f==0xF0) { // F0 usually indicates key up
				if (m==SYMBOL_00)
					m=SYMBOL_F0;
				else
					m=SYMBOL_E0F0;
			} else if (f==0xE0) { // extended key
				m=SYMBOL_E0;
			} else {
				if (display_mode == DISPLAY_SCANCODE) {
					// show modifiers and scancode
					SetMatrixBufferHex2(m,f);

				} else if (display_mode == DISPLAY_CHAR) {
					// show character
					str[3] = ' ';
					if (f==0x14) {
						// ctl: lets ignore which side was pressed
						if (MASK_EQ(m,SYMBOL_F0))
							key_mods &= ~KEY_CTL;
						else
							key_mods |=  KEY_CTL;
					} else if (f==0x11) {
						// alt: lets ignore which side was pressed
						if (MASK_EQ(m,SYMBOL_F0))
							key_mods &= ~KEY_ALT;
						else
							key_mods |=  KEY_ALT;
					} else if (f==0x12 || f==0x59) {
						// shift: lets ignore which side was pressed
						if (MASK_EQ(m,SYMBOL_F0))
							key_mods &= ~KEY_SHIFT;
						else
							key_mods |=  KEY_SHIFT;
					} else if (MASK_EQ(m,SYMBOL_E0)) {
						//the right ALT/CTL have been handled
						//the remaining E0 are the number keys and arrows


					} else if (f>=0x15 && f<=0x65) {
						// the "majority" of symbols fall here....
						if (key_mods&KEY_SHIFT)
							str[3] = sscanchar[f-SCANCHAR_OFFSET];
						else
							str[3] = scanchar[f-SCANCHAR_OFFSET];
;					} else if (f==0x0E) {
						// case with shift modifier that didn't fit well in the scanchar range.
						str[3] = (key_mods&KEY_SHIFT)?'~':'`';
					} else {
						// any of the others I havent accounted for....
						str[3] = '?';


					}
					str[0] = 0x18+key_mods;

					SetMatrixBuffer(str);
				}
				m=SYMBOL_00;
			}
			lw=0;
		}

		// pseudo-timeout 
		if (lw==0x07FF) {
			str[0] = ' ';
			str[1] = ' ';
			str[2] = ' ';
			str[3] = ' ';
			SetMatrixBuffer(str);
        }

	    DrawMatrixBuffer();
		DrawMatrixBuffer();
		Check1Button();
		if (B1PUSHED) {
			b1up = 0;
			display_mode = display_mode == DISPLAY_SCANCODE ? DISPLAY_CHAR : DISPLAY_SCANCODE;
			SetMatrixBuffer("    ");
			DrawMatrixBuffer();
			AnnounceMode();
		}
    }
}


void InitPD1AsInterrupt(void) {
	// Clock INPUT = PD[1] (interupt driven)
	PDDD    |= 0x02; // PD[1] as Input
	PAIMUX  |= 0x02; // PD[1] not PA[1]
	PAIEDGE &=~0x02; // PD[1] IRQ on falling edge
	IRQ1ENH |= 0x02; // PD[1] Interupt (High Bit) as High Priority
	IRQ1ENL |= 0x02; // PD[1] Interupt (Low Bit) as High Priority
	IRQ1SET |= 0x02; // PD[1] Interupt Set
}

void InitPD0AsPolled(void) {
	// Data INPUT = PD[0] (polled?)
	PDDD |= 0x01;
}


short volatile fi = 0;
short volatile parity = 0;

#pragma interrupt
void interrupt pd1_isr(void) {
	short b = (PDIN & 0x01);

	if (fi == 0 && b == 0) {
		// valid frame starts with a logic 0
		// even if we think we are at the start, wait for a zero
		frame = 0;
		SET_FRAME_READ;
		parity = 1;
		++fi;
	} else if (fi >= 1 && fi <= 8) {
		// we now have 8 bits of data
		frame |= b << (fi-1);
		parity += b;
		++fi;
	} else if (fi == 9) {
		// followed by the odd parity
		if ((parity%2)!=b)
			frame_status |= FRAME_ERROR;
 		++fi;
	} else if (fi >= 10) {
		// followed by a logic 1
		frame_status |= FRAME_READY;
		fi = 0;
	}

	SET_VECTOR(P1AD, pd1_isr);
}

void AnnounceMode(void) {
	//
	if (display_mode == DISPLAY_SCANCODE) {
		play_note(1, 'A', 0, 5, 0);
		play_note(32, 'P', 0, 0, 0);
	} else {
		play_note(1, 'D', 0, 5, 0);
		play_note(32, 'P', 0, 0, 0);
	}
}